﻿using NetJavap.ConstantInfos;
using NetJavap.AttributeInfos;
using NetJavap.JVM;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NetJavap
{
    /// <summary>
    /// 表示Java Class文件封装
    /// </summary>
    public class JavaClass : JavaObject, IWithAttributeMember
    {

        private UInt32 _magic;
        private UInt16 _minorVersion;
        private UInt16 _majorVersion;
        private UInt16 _constantPoolCount;
        private Dictionary<UInt16, ConstantInfo> _constantPool;
        private AccessFlags _accessFlags;
        private UInt16 _thisClassIndex;
        private UInt16 _superClassIndex;
        private UInt16 _interfaceCount;
        private UInt16[] _interfaces;
        private UInt16 _fieldsCount;
        private FieldInfo[] _fieldInfo;
        private UInt16 _methodsCount;
        private MethodInfo[] _methods;
        private UInt16 _attributesCount;
        private AttributeInfo[] _attributes;

        private String _fullName;
        private ConstantClassInfo _thisClassInfo;
        private ConstantClassInfo _superClassInfo;
        private JavaClass _superClass;
        private bool _isStaticInitCalled = false;

        public Dictionary<UInt16, ConstantInfo> ConstantPool { get { return this._constantPool; } }
        public MethodInfo[] Methods { get { return this._methods; } }
        public FieldInfo[] Fields { get { return this._fieldInfo; } }
        public AttributeInfo[] Attributes
        {
            get { return this._attributes; }
        }
        public ConstantClassInfo ThisClassInfo
        {
            get
            {
                if (this._thisClassInfo == null)
                {
                    this._thisClassInfo = this.ConstantPool[this._thisClassIndex] as ConstantClassInfo;
                }
                return this._thisClassInfo;
            }
        }
        public ConstantClassInfo SuperClassInfo
        {
            get
            {
                if (this._superClassInfo == null)
                {
                    if (this._superClassIndex != 0)
                    {
                        this._superClassInfo = this.ConstantPool[this._superClassIndex] as ConstantClassInfo;
                    }
                }
                return this._superClassInfo;
            }
        }
        public JavaClass SuperClass
        {
            get
            {
                if (this._superClass == null)
                {
                    if (this._superClassIndex != 0)
                    {
                        this._superClass = JVMClassLoader.LoadClass(this.SuperClassInfo.TypeInfo.Name);
                    }
                }
                return this._superClass;
            }
        }
        public String FullName
        {
            get
            {
                if (this._fullName == null)
                {
                    this._fullName = this.ThisClassInfo.TypeInfo.Name;
                }
                return this._fullName;
            }
        }

        public UInt16 GetFieldId(String name)
        {
            UInt16 index = 0;
            UInt16 i;
            for (i = 0; i < this.Fields.Length; i++)
            {
                FieldInfo field = this.Fields[i];
                if (field.Name.Value == name)
                {
                    index = i;
                    break;
                }
            }
            if (i == this.Fields.Length)
            {
                throw new InvalidOperationException("静态字段节点指向无法确定！");
            }
            return index;
        }
        public Object GetStaticValue(String name)
        {
            Dictionary<UInt16, Object> thisFieldCache = JavaObject.GetStaticFieldCache(this);
            Object staticValue;
            UInt16 fieldId = this.GetFieldId(name);
            if (!thisFieldCache.TryGetValue(fieldId, out staticValue))
            {
                //变量槽置空
                thisFieldCache[fieldId] = null;
            }
            return staticValue;
        }
        public void SetStaticValue(String name, Object value)
        {
            JavaObject.GetStaticFieldCache(this)[this.GetFieldId(name)] = value;
        }
        public MethodInfo GetMethod(string methodName, TypeInfo retval, params TypeInfo[] pars)
        {
            foreach (var method in this.Methods)
            {
                if (method.Name.Value == methodName && method.Parameters.Length == pars.Length)
                {
                    bool isParsMatch = true;
                    for (int i = 0; i < method.Parameters.Length; i++)
                    {
                        if (!method.Parameters[i].Equals(pars[i]))
                        {
                            isParsMatch = false;
                        }
                    }
                    if (isParsMatch)
                    {
                        return method;
                    }
                }
            }
            return null;
        }

        public JavaClass(BinaryReader reader)
            : base(null)
        {
            if (reader == null)
            {
                return;
            }
            this._magic = reader.ReadUInt32BE();
            this._minorVersion = reader.ReadUInt16BE();
            this._majorVersion = reader.ReadUInt16BE();
            this._constantPoolCount = reader.ReadUInt16BE();
            this._constantPool = ArrayBuilder.BuildConstants(reader, this, this._constantPoolCount);
            this._accessFlags = (AccessFlags)reader.ReadUInt16BE();
            this._thisClassIndex = reader.ReadUInt16BE();
            this._superClassIndex = reader.ReadUInt16BE();
            this._interfaceCount = reader.ReadUInt16BE();
            this._interfaces = ArrayBuilder.BuildArray(() => reader.ReadUInt16BE(), this._interfaceCount);
            this._fieldsCount = reader.ReadUInt16BE();
            this._fieldInfo = ArrayBuilder.BuildArray(() => new FieldInfo(reader, this), this._fieldsCount);
            this._methodsCount = reader.ReadUInt16BE();
            this._methods = ArrayBuilder.BuildArray(() => new MethodInfo(reader, this), this._methodsCount);
            this._attributesCount = reader.ReadUInt16BE();
            this._attributes = ArrayBuilder.BuildAttributes(reader, this, this._attributesCount);
        }

        public override string ToString()
        {
            return _accessFlags.ToFormattedString() + " class " + (
                this._constantPool[this._thisClassIndex] as ConstantClassInfo).TypeInfo.ToString();
        }

        /// <summary>
        /// 调用类自身静态构造函数
        /// </summary>
        public void InvakeStaticInit()
        {
            if (!_isStaticInitCalled)
            {

                MethodInfo sinitInfo = this.GetMethod("<clinit>", new TypeInfo("V", 0));
                if (sinitInfo != null)
                {
                    CodeAttributeInfo code = sinitInfo.GetAttribute<CodeAttributeInfo>();
                    JVMCodeRunner runner = new JVMCodeRunner(code);
                    while (!runner.ExecuteSetp()) ;
                }
            }
            _isStaticInitCalled = true;
        }
    }
}
